package param

import (
	"encoding"
	"fmt"
	"reflect"
)

// supported data type
const (
	String          = reflect.String
	Int             = reflect.Int
	Int32           = reflect.Int32
	Int64           = reflect.Int64
	Float32         = reflect.Float32
	Float64         = reflect.Float64
	Bool            = reflect.Bool
	File            = reflect.UnsafePointer + 1 + iota // implement 'param.FileHandler'
	TextUnmarshaler                                    // implement 'encoding.TextUnmarshaler'
	SliceString                                        // []string
	SliceInt                                           // []int
	SliceInt32                                         // []int32
	SliceInt64                                         // []int64
	SliceFloat32                                       // []float32
	SliceFloat64                                       // []float64
	SliceBool                                          // []bool
	Unsupported
	UnsupportedSlice
)

// 自定义反射类型
type ReflectType struct {
	reflect.Type
	fields []reflect.StructField
	kinds  []reflect.Kind
	validator *Validator
}

// 参数名称提供器
type FieldNameProvider struct {
	key string
	ignore []string
}

// 当前字段的参数是否被忽略
func (fnp *FieldNameProvider) Ignore(field reflect.StructField) bool {
	if field.Tag.Get(fnp.key) == "-" {
		return true
	} else {
		for _, i := range fnp.ignore {
			if fnp.FieldName(field) == i {
				return true
			}
		}
	}
	return false
}

// 获得当前字段的参数名
func (fnp *FieldNameProvider) FieldName(field reflect.StructField) string {
	if field.Tag.Get(fnp.key) == "-" {
		return ""
	} else if name := field.Tag.Get(fnp.key); name != "" {
		return name
	} else {
		return field.Name
	}
}

//var ParamNamePvd = &FieldNameProvider{key: "param"}

func NewFieldNameProvider(ignore []string) *FieldNameProvider {
	return &FieldNameProvider{
		key: "param",
		ignore:ignore,
	}
}

var JsonNamePvd = &FieldNameProvider{key: "json", ignore:[]string{"-"}}

var XmlNamePvd = &FieldNameProvider{key: "xml", ignore:[]string{"-"}}

// FieldHandler 定义了处理字段的回掉函数类型
type FieldHandler func(kind reflect.Kind, field reflect.StructField, cdt ConditionProvider)

// 遍历字段类型
func (rt *ReflectType) Range(handler FieldHandler) {
	for idx, field := range rt.fields {
		offset := field.Offset
		var cdt *condition
		if rt.validator != nil {
			if rt.validator.conditions != nil {
				if c := rt.validator.conditions[offset]; c != nil {
					cdt = c
				}
			}
		}
		handler(rt.kinds[idx], field, cdt)
	}
}

// 获取 struct 的字段偏移量及字段类型
func structFields(structural reflect.Type, fieldsOffset uintptr) (fields []reflect.StructField) {
	fieldNum := structural.NumField()
	for idx := 0; idx < fieldNum; idx++ {
		field := structural.Field(idx)
		// 将子字段的偏移量加上父字段的偏移量
		field.Offset += fieldsOffset
		kind := field.Type.Kind()
		if kind == reflect.Struct {
			subFields := structFields(field.Type, field.Offset)
			fields = append(fields, subFields...)
		} else {
			fields = append(fields, field)
		}
	}
	return
}

// 反射获得自定义的字段类型, validator 为自定义验证器, 标签验证条件将覆盖自定义验证器的验证条件
func ReflectTypeOf(schema interface{}, pvd *FieldNameProvider, validator *Validator) *ReflectType {
	t := reflect.TypeOf(schema)
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		panic(fmt.Errorf("need struct, got %v", t))
	}
	if validator == nil {
		validator = NewEmptyValidator(schema)
	}
	rt := &ReflectType{
		Type:   t,
		validator:validator,
	}
	fields := structFields(t, 0)
	for _, field := range fields {
		if pvd == nil || !pvd.Ignore(field) {
			if pvd != nil {
				field.Name = pvd.FieldName(field)
			}
			var kind reflect.Kind
			switch k := field.Type.Kind(); k {
			case reflect.String, reflect.Int, reflect.Int32, reflect.Int64, reflect.Float32,
				reflect.Float64, reflect.Bool:
				kind = k
			case reflect.Interface, reflect.Ptr:
				var transformerType= reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
				if field.Type.ConvertibleTo(transformerType) {
					kind = TextUnmarshaler
				} else {
					kind = Unsupported
				}
			case reflect.Func:
				var fileHandlerType= reflect.TypeOf(new(FileHandler)).Elem()
				if field.Type.ConvertibleTo(fileHandlerType) {
					kind = File
				} else {
					kind = Unsupported
				}
			case reflect.Slice:
				element := field.Type.Elem()
				switch element.Kind() {
				case reflect.String:
					kind = SliceString
				case reflect.Int:
					kind = SliceInt
				case reflect.Int32:
					kind = SliceInt32
				case reflect.Int64:
					kind = SliceInt64
				case reflect.Float32:
					kind = SliceFloat32
				case reflect.Float64:
					kind = SliceFloat64
				case reflect.Bool:
					kind = SliceBool
				default:
					kind = UnsupportedSlice
				}
			default:
				kind = Unsupported
			}
			// rt.fields 的 key 与 rt.kinds 的 key 必须对应
			rt.fields = append(rt.fields, field)
			rt.kinds = append(rt.kinds, kind)

			// 添加标签条件
			if kind != Unsupported && kind != UnsupportedSlice {
				if isCondition(field.Tag) {
					var cdt *condition
					var ok bool
					if cdt, ok = validator.conditions[field.Offset]; !ok{
						cdt = validator.offsetField(field.Offset)
					}
					// tag 标签条件将覆盖验证器条件
					err := cdt.Syntax(string(field.Tag))
					if err != nil{
						panic(err)
					}
					if cdt.id == ""{
						cdt.id = field.Name
					}
				}
			}
		}
	}
	return rt
}
